/**
 * 
 */
package com.da.landlord;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.da.util.RoomImportUtils;
import com.espirit.eap.manager.EapImport;
import com.espirit.eap.util.StatGenericJPAService;
import com.espirit.eap.util.StatGenericJPAService.TypeConverter;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.security.SafeManager;
import com.googlecode.cswish.struts.ServletRedirectResult;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.ClassUtil;
import com.googlecode.cswish.util.Functions;
import com.googlecode.cswish.util.bean.ModelUtil;
import com.opensymphony.xwork2.Result;
import com.ra.external.RoomRetrievalService;
import com.ra.landlord.Agreement;
import com.ra.landlord.Building;
import com.ra.landlord.BuildingFloor;
import com.ra.landlord.FeeRule;
import com.ra.landlord.FeeRuleCfg;
import com.ra.landlord.FeeRuleCfgCalc;
import com.ra.landlord.FeeRuleCfgDesc;
import com.ra.landlord.LandlordOwner;
import com.ra.landlord.LandlordOwnerUser;
import com.ra.landlord.LandlordUser;
import com.ra.landlord.LandlordUserService;
import com.ra.landlord.Meter;
import com.ra.landlord.Room;
import com.ra.landlord.TenantUser;
import com.ra.landlord.ValidAgreement;
import com.ra.landlord.constant.FeeRuleCalcMode;
import com.ra.landlord.constant.FeeRuleType;
import com.ra.system.Version;
import com.ra.util.Address;
import com.ra.util.AddressService;

/**
 * 批量导入房间
 * @author Jebel Qin
 * @date 2015年9月17日
 * @since v4.0
 */

@Service
public class RoomImportService 
{
	private static final Log logger = LogFactory.getLog(RoomImportService.class);

	@Resource
	private StatGenericJPAService genericService;
	
	@Resource
	private AddressService addressService;
	
	@Resource
	private SafeManager safeManager;
	
	@Resource
	private Functions functions;
	
	@Resource
	private LandlordUserService landlordUserService;
	
	@Resource
	private RoomRetrievalService roomRetrievalService;
	
	private RoomImportService roomImportService;
	
	private RoomImportService getThis()
	{
		if (roomImportService == null)
		{
			roomImportService = ObjectFactory.getInstance().getBean(RoomImportService.class, false);
		}
		return roomImportService;
	}
	
	/**
	 * 类型转换接口: 房型/压付模式/性别
	 */
	private TypeConverter typeConverter = new TypeConverter() 
	{
		@Override
		public Object convertValue(Object target, String propertyName, Object value) {
			if ("roomType".equals(propertyName)) 
			{
				value = RoomImportUtils.getRoomType((String)value);
			} 
			else if ("agreement.rentType".equals(propertyName)) {		// 0:一押一付；1:两押一付；2：三押一付；3：四押一付
				value = RoomImportUtils.getRentType((String)value);
			} 
			else if ("agreement.tenantUser.gender".equals(propertyName)) {
				value = RoomImportUtils.getGenderType((String)value);
			}
			
			return value;
		}
	};
	
	/**
	 * 为返回成功信息加入的方法
	 * @param successMsg
	 */
	public void _importData(String successMsg)
	{
		
	}
	
	/**
	 * 导入楼房数据
	 * @param model
	 * @return
	 */
	public Result importData(EapImport model) 
	{
		if (model.getFile() == null) 
		{
			logger.error("importData: No excel file for importing.");
			throw new ApplicationException("未发现待导入的excel文件！");
		}
		StringBuilder successMsg = new StringBuilder(128);
		List<Room> allUpdatedRooms = getThis().importDataInTrans(model, successMsg);
		
		roomRetrievalService.batchPublishRooms(allUpdatedRooms, false);
		
		//ActionContext.getContext().getValueStack().setValue("successMsg", successMsg.toString());
		String msg = "";
		try 
		{
			msg =  URLEncoder.encode(successMsg.toString(), "UTF-8");
		} 
		catch (UnsupportedEncodingException e) 
		{
			logger.error("Fail to encode url encode string: " + successMsg.toString(), e);
		}
		
		Result result = new ServletRedirectResult("/da/landlord/roomImport._importData.html?successMsg=" + msg);
		ObjectFactory.getInstance().autoWireBean(result);
		return result;
	}
	
	private boolean checkTemplateFile(EapImport model, int columCont)
	{
		boolean isLastNew = false;
		FileInputStream input = null;
		try 
		{
			File file = model.getFile();
			
			input = new FileInputStream(file);
			Workbook book;
			if (model.getFileFileName().endsWith("xls")) 
			{
				book = new HSSFWorkbook(new POIFSFileSystem(input));
			} 
			else 
			{
				book = new XSSFWorkbook(input);
			}
			
			// get a Sheet object.
			Sheet sheet = book.getSheetAt(0);
			Row sheetRow = sheet.getRow(0);
			int cellCount = sheetRow.getLastCellNum();
			isLastNew = (cellCount == columCont);
		}
		catch (Exception ex) {
			logger.error("Fail to import excel file", ex);
			
		} finally {  
            try {  
                if (input != null) {  
                    input.close();  
                }
            } catch (IOException ex) { 
            	logger.error("Fail to close excel file", ex);
            }  
        }
		
		return isLastNew;
	}

	@Transactional
	public List<Room> importDataInTrans(EapImport model, StringBuilder successMsg) {
		LandlordOwnerUser landlordUser = (LandlordOwnerUser)landlordUserService.getLoginUser();
		
		//是否使用了新模板(新模板多2个计费方式)
		boolean isLastNewFile = checkTemplateFile(model, 27);
		
		int[] mappingColumns = null;
		List<String> propertyNames = null;
		if (isLastNewFile)
		{
			propertyNames = Arrays.asList("building.name", "building.detailAddress", "name", "roomType", "buildingFloor.no", "area"
				, "agreement.tenantUser.name", "agreement.tenantUser.gender", "agreement.tenantUser.phone", "agreement.tenantUser.idCard"
				, "agreement.rentBeginDate", "agreement.rentEndDate", "agreement.rentPeriod", "agreement.rentCycle", "agreement.rentType", "agreement.rentPay", "agreement.deposit"
				, "feeRules[0].feeRuleCfg.feeRuleCfgCalc.price", "feeRules[1].feeRuleCfg.feeRuleCfgCalc.price", "feeRules[2].feeRuleCfg.feeRuleCfgCalc.price", "feeRules[3].feeRuleCfg.feeRuleCfgCalc.price"
				, "feeRules[4].meters[0].count", "feeRules[4].feeRuleCfg.feeRuleCfgCalc.price"
				, "feeRules[5].meters[0].count", "feeRules[5].feeRuleCfg.feeRuleCfgCalc.price"
				, "feeRules[6].meters[0].count", "feeRules[6].feeRuleCfg.feeRuleCfgCalc.price");
		}
		else
		{
			propertyNames = Arrays.asList("building.name", "building.detailAddress", "name", "roomType", "buildingFloor.no", "area"
					, "agreement.tenantUser.name", "agreement.tenantUser.gender", "agreement.tenantUser.phone", "agreement.tenantUser.idCard"
					, "agreement.rentBeginDate", "agreement.rentEndDate", "agreement.rentPeriod", "agreement.rentCycle", "agreement.rentType", "agreement.rentPay", "agreement.deposit"
					, "feeRules[0].feeRuleCfg.feeRuleCfgCalc.price", "feeRules[1].feeRuleCfg.feeRuleCfgCalc.price", "feeRules[2].feeRuleCfg.feeRuleCfgCalc.price"
					, "feeRules[3].meters[0].count", "feeRules[3].feeRuleCfg.feeRuleCfgCalc.price"
					, "feeRules[4].meters[0].count", "feeRules[4].feeRuleCfg.feeRuleCfgCalc.price");
		}
		
		// 管理费,卫生费,网费,水表刻度(吨),水表单价(元/吨),电表刻度(度),电表单价(元/度)
		model.setMappingColumns(mappingColumns);
		List<Room> rooms = genericService.convertFileToModelList(model, Room.class, propertyNames, typeConverter);
		
		checkFileData(rooms);
		
		// update or insert these building
		Map<String, Building> buildings = new HashMap<>();
		Map<String, BuildingFloor> floors = new HashMap<>();
		Map<String, FeeRuleCfg> ruleMapping = new HashMap<>();
		Map<String, FeeRuleCfgDesc> descMapping = new HashMap<>();
		
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		for (Room room : rooms) {
			Building building = room.getBuilding();
			fixModelIdAndVersion(building, landlordUser, new String[] {"name"}, 0);
						
			String buildingName = building.getName();
			Building existed = buildings.get(buildingName);
			if (existed == null) {
				existed = building;
				buildings.put(buildingName, building);
				List<Room> roomList = new ArrayList<>();
				building.setRooms(roomList);
				building.getRooms().add(room);
				
				List<LandlordUser> landlordUsersList = new ArrayList<>();
				landlordUsersList.add(landlordUser);
				building.setLandlordUsers(landlordUsersList);
				if (building.getDetailAddress() != null) {
					List<Address> addressList = addressService.searchBaiduPlaceByKeyword(building.getDetailAddress(), null, null, 1, 1);
					if (addressList.size() > 0) {
						Address address = addressList.get(0);
						building.setAddress(address);
						
						room.setAddress(address);
					}
				}
				building.setBuildingFloors(new ArrayList<BuildingFloor>());
			} else {
				room.setBuilding(existed);
				room.setAddress(existed.getAddress());
				existed.getRooms().add(room);
			}
			room.setRentState(Room.RENT_STATE_ALREADY);
			room.setRentType(0);
			room.adaptRoomType();
			
			fixModelIdAndVersion(room, landlordUser, new String[] {"name", "building.id"}, 0);
			
			BuildingFloor floor = room.getBuildingFloor();
			String key = room.getBuilding().getName() + '#' + floor.getNo();
			BuildingFloor existedFloor = floors.get(key);
			if (existedFloor == null) {
				floors.put(key, floor);
				
				existed.getBuildingFloors().add(floor);
				floor.setName("第" + (floor.getNo()) + "层");
				floor.setBuilding(existed);
				floor.setType(0);
				fixModelIdAndVersion(floor, landlordUser, new String[] {"no", "building.id"}, 0);
			} else {
				room.setBuildingFloor(existedFloor);
			}
			
			ValidAgreement agreement = room.getAgreement();
			if (agreement != null)
			{
				agreement.setRoom(room);
				agreement.setRentCycleType(Agreement.RENT_CYCLE_TYPE_MONTH);
				if (agreement.getRentPay() == null) {
					agreement.setRentPay(new BigDecimal(0));
				}
				if (agreement.getRentType() == null) {
					agreement.setRentType(0);
				}
				if (StringUtils.isEmpty(agreement.getDeposit())) {
					agreement.setDeposit("0");
				} else if (!NumberUtils.isDigits(agreement.getDeposit())) {
					if (logger.isDebugEnabled()) {
						logger.debug("Skip the invalid deposit: " + agreement.getDeposit() + ", room.name=" + room.getName());
					}
					agreement.setDeposit("0");
				}
				// fix the format of begin date and end date
				Date rentBeginDate = null;
				if (agreement.getRentBeginDate() != null) {
					try {
						rentBeginDate = DateUtils.parseDate(agreement.getRentBeginDate(), new String[] {"yyyy-MM-dd", "yyyy/MM/dd", "yyyy.MM.dd"});
					} catch (Exception e) {
						logger.error("Invalid date:" + agreement.getRentBeginDate(), e);
					}
				}
				if (rentBeginDate == null) {
					rentBeginDate = Calendar.getInstance().getTime();
				}
				agreement.setRentBeginDate(df.format(rentBeginDate));
				
				Date rentEndDate = null;
				if (agreement.getRentEndDate() != null) {
					try {
						rentEndDate = DateUtils.parseDate(agreement.getRentEndDate(), new String[] {"yyyy-MM-dd", "yyyy/MM/dd", "yyyy.MM.dd"});
					} catch (Exception e) {
						logger.error("Invalid date:" + agreement.getRentBeginDate(), e);
					}
				}
				if (rentEndDate == null) {
					if (NumberUtils.isDigits(agreement.getRentPeriod())) {
						Integer period = new Integer(agreement.getRentPeriod());
						Calendar calBegin = Calendar.getInstance();
						calBegin.setTime(rentBeginDate);
						calBegin.add(Calendar.MONTH, period);
						rentEndDate = calBegin.getTime();
					} else {
						rentEndDate = Calendar.getInstance().getTime();
					}
				}
				agreement.setRentEndDate(df.format(rentEndDate));
				
				if (agreement.getRentCycle() == null) {
					agreement.setRentCycle(0);			//1
				} else {
					agreement.setRentCycle(agreement.getRentCycle() - 1);
				}
				if (agreement.getRentPeriod() == null) {
					Calendar calBegin = Calendar.getInstance();
					calBegin.setTime(rentBeginDate);
					Calendar calEnd = Calendar.getInstance();
					calEnd.setTime(rentEndDate);
					int gap = 12 * (calEnd.get(Calendar.YEAR) - calBegin.get(Calendar.YEAR)) + (calEnd.get(Calendar.MONTH) - calBegin.get(Calendar.MONTH));
					if (gap > 0) {
						agreement.setRentPeriod(String.valueOf(gap));
					} else {
						agreement.setRentPeriod("12");
					}
				}
				agreement.adaptRentPeriod();
				agreement.adaptRentType();
				fixModelIdAndVersion(agreement, landlordUser, new String[] {"room.id"}, 0);
				room.setRental(functions.s(agreement.getRentPay()));
	
				TenantUser tenantUser = agreement.getTenantUser();
				tenantUser.setRegister(Calendar.getInstance().getTime());
				tenantUser.setAgreements(new ArrayList<Agreement>());
				tenantUser.getAgreements().add(agreement);
				fixModelIdAndVersion(tenantUser, landlordUser, new String[] {"agreement.id"}, 0);
			}
			
			// room.feeRules
			if (room.getFeeRules() != null && room.getFeeRules().size() > 0) {
				// fix data
				if (isLastNewFile)
				{
					initFeeRuleCfg(room, 0, "管理费", FeeRuleType.fixed, "元", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 1,  "卫生费", FeeRuleType.fixed, "元", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 2,  "网费", FeeRuleType.fixed, "元", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 3,  "有线电视费", FeeRuleType.fixed, "元", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 4,  "水表", FeeRuleType.meter, "吨", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 5,  "电表", FeeRuleType.meter, "度", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 6,  "热水费", FeeRuleType.meter, "立方", landlordUser, ruleMapping, descMapping);
				}
				else
				{
					initFeeRuleCfg(room, 0, "管理费", FeeRuleType.fixed, "元", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 1,  "卫生费", FeeRuleType.fixed, "元", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 2,  "网费", FeeRuleType.fixed, "元", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 3,  "水表", FeeRuleType.meter, "吨", landlordUser, ruleMapping, descMapping);
					initFeeRuleCfg(room, 4,  "电表", FeeRuleType.meter, "度", landlordUser, ruleMapping, descMapping);
				}

				// remove the invalid null value
				Iterator<FeeRule> feeRuleIter = room.getFeeRules().iterator();
				while (feeRuleIter.hasNext()) {
					FeeRule feeRule = feeRuleIter.next();
					if (!isValidFeeRule(feeRule)) {
						feeRuleIter.remove();
					}
				}
			}
		}
		
		//Add FeeRuleCfgDesc and FeeRuleCfg
		Map<String, FeeRuleCfg> dbFeeRuleCfgMap = new HashMap<String, FeeRuleCfg>(ruleMapping.size());
		if (!descMapping.isEmpty())
		{
			FeeRuleCfg dbFeeRuleCfg;
			for (FeeRuleCfg feeRuleCfg : ruleMapping.values()) 
			{
				if (feeRuleCfg.getId() == null)
				{
					dbFeeRuleCfg = genericService.add(feeRuleCfg);
				}
				else
				{
					dbFeeRuleCfg = genericService.merge(feeRuleCfg);
				}
				dbFeeRuleCfgMap.put(dbFeeRuleCfg.getIdentifyKey(), dbFeeRuleCfg);
			}
		}

		List<Room> allUpdatedRooms = new ArrayList<Room>();
		boolean hasAppendMsg = false;
		for (Building building : buildings.values()) {
			autoSetRoomFeeRules(dbFeeRuleCfgMap, building.getRooms());
			building.setRoomCount(building.getRooms().size());
			//building.setFloorCount(building.getBuildingFloors().size());
			setBuildingMaxFloorNo(building);
			if (building.getId() == null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Create building for user: " + landlordUser.getPhone() + ", name=" + building.getName());
				}
				
				Building addedBuilding = genericService.add(building);
				allUpdatedRooms.addAll(addedBuilding.getRooms());
				addFeeRuleMeters(addedBuilding, building);
				
				appendBreakLine(hasAppendMsg, successMsg);
				successMsg.append("新增楼房：").append(building.getName()).append(", 共").append(building.getRooms().size()).append("套房间。");
				hasAppendMsg = true;
			} else {
				if (logger.isDebugEnabled()) {
					logger.debug("Update building: id=" + building.getId() + ", name=" + building.getName());
				}
				
				Building updatedBuilding = genericService.update(building, true, null, null);
				allUpdatedRooms.addAll(updatedBuilding.getRooms());
				addFeeRuleMeters(updatedBuilding, building);
				
				appendBreakLine(hasAppendMsg, successMsg);
				successMsg.append("更新楼房：").append(building.getName()).append(", 共").append(building.getRooms().size()).append("套房间。");
				hasAppendMsg = true;
			}
		}
		return allUpdatedRooms;
	}
	
	private void setBuildingMaxFloorNo(Building building)
	{
		List<Room> rooms = building.getRooms();
		int maxFloorNo = 1;
		for (Room room : rooms) 
		{
			if (room.getBuildingFloor().getNo() > maxFloorNo)
			{
				maxFloorNo = room.getBuildingFloor().getNo();
			}
		}
		
		building.setFloorCount(maxFloorNo);
	}
	
	private void addFeeRuleMeters(Building updatedBuilding, Building building)
	{
		int roomIndex = 0;
		List<Room> updatedRooms = updatedBuilding.getRooms();
		List<Room> rooms = building.getRooms();
		for (Room room : rooms) 
		{
			Room updatedRoom = updatedRooms.get(roomIndex);
			List<FeeRule> updatedFeeRules = updatedRoom.getFeeRules();
			roomIndex++;
			
			if (updatedFeeRules == null || updatedFeeRules.isEmpty())
			{
				continue;
			}
			
			int feeRuleIndex = 0;
			List<FeeRule> feeRules = room.getFeeRules();
			if (feeRules == null || feeRules.isEmpty())
			{
				continue;
			}
			for (FeeRule feeRule : feeRules) 
			{
				FeeRule updatedFeeRule = updatedFeeRules.get(feeRuleIndex);
				feeRuleIndex++;
				
				List<Meter> meters = feeRule.getMeters();
				if (meters != null && (updatedFeeRule != null))
				{
					if (updatedFeeRule.getMeters() != null && !updatedFeeRule.getMeters().isEmpty() && updatedFeeRule.getMeter(0).getId() != null)
					{
						continue;
					}
					
					for (Meter meter : meters) 
					{
						meter.setFeeRule(updatedFeeRule);
						genericService.add(meter);
					}
				}
			}
		}
	}
	
	private void checkFileData(List<Room> rooms)
	{
		if (rooms == null || rooms.isEmpty())
		{
			throw new ApplicationException("文件没有任何需要导入的数据!");
		}
		
		StringBuilder alertMsg = new StringBuilder(128);
		boolean hasError = false;
		int index = 2;
		for (Room room : rooms) 
		{
			if (StringUtils.isBlank(room.getBuilding().getName()))
			{
				alertMsg.append("楼房名称不能为空！");
				hasError = true;
			}
			if (StringUtils.isBlank(room.getBuilding().getDetailAddress()))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("楼房地址不能为空！");
				hasError = true;
			}
			if (StringUtils.isBlank(room.getName()))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("房名不能为空！");
				hasError = true;
			}
			if (room.getBuildingFloor().getNo() == null)
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("所在楼层不能为空！");
				hasError = true;
			}
			if (room.getAgreement() != null  && (room.getAgreement().getTenantUser() == null || StringUtils.isBlank(room.getAgreement().getTenantUser().getName())))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("租客姓名不能为空！");
				hasError = true;
			}
			if (room.getAgreement() != null  && (room.getAgreement().getTenantUser() == null || StringUtils.isBlank(room.getAgreement().getTenantUser().getPhone())))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("手机号不能为空！");
				hasError = true;
			}
			/*if (room.getAgreement() != null  && room.getAgreement().getTenantUser() != null && StringUtils.isBlank(room.getAgreement().getTenantUser().getIdCard()))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("身份证不能为空！");
				hasError = true;
			}*/
			
			if (room.getAgreement() != null && StringUtils.isBlank(room.getAgreement().getRentBeginDate()))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("起租日期不能为空！");
				hasError = true;
			}
			if (room.getAgreement() != null && StringUtils.isBlank(room.getAgreement().getRentEndDate()))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("到租日期不能为空！");
				hasError = true;
			}
			if (room.getAgreement() != null && StringUtils.isBlank(room.getAgreement().getRentPeriod()))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("租期不能为空！");
				hasError = true;
			}
			else if (room.getAgreement() != null && !NumberUtils.isDigits(room.getAgreement().getRentPeriod()))
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("租期必须为数字！");
				hasError = true;
			}
			if (room.getAgreement() != null && room.getAgreement().getRentCycle() == null)
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("收费周期不能为空！");
				hasError = true;
			}
			
			if (room.getAgreement() != null && room.getAgreement().getRentPay() == null)
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("租金不能为空！");
				hasError = true;
			}
			if (room.getAgreement() != null && room.getAgreement().getDeposit() == null)
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("押金不能为空！");
				hasError = true;
			}
			
			if (hasError)
			{
				appendBreakLine(hasError, alertMsg);
				alertMsg.append("错误行数：" + index);
				break;
			}
			
			index++;
		}
		
		if (alertMsg.length() > 0)
		{
			logger.error("checkFileData: File data format error:" + alertMsg.toString());
			throw new ApplicationException(alertMsg.toString());
		}
	}
	
	private void appendBreakLine(boolean hasError, StringBuilder alertMsg)
	{
		if (hasError)
		{
			alertMsg.append("<br>");
		}
	}
	
	private void autoSetRoomFeeRules(Map<String, FeeRuleCfg> dbFeeRuleCfgMap, List<Room> rooms)
	{
		if (dbFeeRuleCfgMap.isEmpty() || rooms.isEmpty())
		{
			return;
		}
		for (Room room : rooms) 
		{
			List<FeeRule> feeRules = room.getFeeRules();
			if (feeRules == null || feeRules.isEmpty())
			{
				continue;
			}
			
			for (FeeRule feeRule : feeRules) 
			{
				FeeRuleCfg feeRuleCfg = feeRule.getFeeRuleCfg();
				FeeRuleCfg dbFeeRuleCfg = dbFeeRuleCfgMap.get(feeRuleCfg.getIdentifyKey());
				if (dbFeeRuleCfg == null)
				{
					continue;
				}
				feeRule.setFeeRuleCfg(dbFeeRuleCfg);
			}
			
		}
	}

	private boolean isValidFeeRule(FeeRule feeRule) {
		boolean valid = false;
		if (feeRule != null) {
			if (feeRule.getFeeRuleCfg() != null) {
				FeeRuleCfgCalc feeRuleCfgCalc = feeRule.getFeeRuleCfg().getFeeRuleCfgCalc();
				if (feeRuleCfgCalc != null) {
					if (feeRuleCfgCalc.getPrice() != null) {
						valid = true;
					}
				}
			}
		}
		return valid;
	}

	private void initFeeRuleCfg(Room room, int ruleIndex, String name, FeeRuleType type, String unit, LandlordOwnerUser landlordUser, Map<String, FeeRuleCfg> ruleMapping, Map<String, FeeRuleCfgDesc> descMapping) {
		List<FeeRule> feeRules = room.getFeeRules();
		FeeRule feeRule = feeRules.size() > ruleIndex ? feeRules.get(ruleIndex) : null;
		if (!isValidFeeRule(feeRule)) {
			return ;
		}
		
		// 1. fee rule
		feeRule.setRoom(room);
		FeeRuleCfgDesc feeRuleCfgDesc = new FeeRuleCfgDesc();
		feeRuleCfgDesc.setName(name);
		feeRule.getFeeRuleCfg().setFeeRuleCfgDesc(feeRuleCfgDesc);
		fixModelIdAndVersion(feeRule, landlordUser, new String[] {"room.id", "feeRuleCfg.feeRuleCfgDesc.name"}, 0);
		
		// 2. fee rule cfg
		FeeRuleCfg feeRuleCfg = feeRule.getFeeRuleCfg();
		String feeRuleCfgKey = name + '#' + feeRuleCfg.getFeeRuleCfgCalc().getPrice();
		FeeRuleCfg existedFeeRuleCfg = ruleMapping.get(feeRuleCfgKey);
		if (existedFeeRuleCfg != null) {
			feeRule.setFeeRuleCfg(existedFeeRuleCfg);
		} else {
			ruleMapping.put(feeRuleCfgKey, feeRuleCfg);
			
			fixModelIdAndVersion(feeRuleCfg, landlordUser, new String[] {"feeRuleCfgDesc.name", "feeRuleCfgCalc.price"}, 0);
			feeRuleCfg.setType(0);
			
			// 3. fee rule description
			String feeRuleCfgDescKey = name;
			FeeRuleCfgDesc existedFeeRuleCfgDesc = descMapping.get(feeRuleCfgDescKey);
			if (existedFeeRuleCfgDesc != null) {
				feeRuleCfg.setFeeRuleCfgDesc(existedFeeRuleCfgDesc);
				existedFeeRuleCfgDesc.getFeeRuleCfgs().add(feeRuleCfg);
			} else {
				descMapping.put(feeRuleCfgDescKey, feeRuleCfgDesc);
				fixModelIdAndVersion(feeRuleCfgDesc, landlordUser, new String[] {"name"}, 0);
				
				feeRuleCfgDesc.setCalcMode(FeeRuleCalcMode.normal);
				//feeRuleCfgDesc.setInitialValue(null);
				feeRuleCfgDesc.setIsShare(Boolean.FALSE);
				//feeRuleCfgDesc.setLossValue(null);
				//feeRuleCfgDesc.setMagnification(new BigDecimal(1));
				feeRuleCfgDesc.setName(name);
				feeRuleCfgDesc.setType(type);
				feeRuleCfgDesc.setUnit(unit);
				feeRuleCfgDesc.setFeeRuleCfgs(new ArrayList<FeeRuleCfg>());
				feeRuleCfgDesc.getFeeRuleCfgs().add(feeRuleCfg);
			}
			
			// 4. fee rule calc
			FeeRuleCfgCalc feeRuleCfgCalc = feeRuleCfg.getFeeRuleCfgCalc();
			// fee fule calc depends on the feeRuleCfg, don't use 'fixModelIdAndVersion'
			if (feeRuleCfg.getId() != null) {
				Integer calcId = genericService.load(feeRuleCfg).getFeeRuleCfgCalc().getId();
				feeRuleCfgCalc.setId(calcId);
			}
			feeRuleCfgCalc.setLandlordUser(landlordUser);
			feeRuleCfgCalc.setStatus(Version.STATUS_VALID);
			feeRuleCfgCalc.setVersion(feeRuleCfg.getVersion());
			
			feeRuleCfgCalc.setRentCycle(1);
			feeRuleCfgCalc.setRentCycleType(0);
			feeRuleCfgCalc.setType(0);
		}
		
		if (feeRule.getMeters() != null && feeRule.getMeters().size() > 0) {
			Meter meter = feeRule.getMeters().get(0);
			meter.setFeeRule(feeRule);
			fixModelIdAndVersion(meter, landlordUser, new String[] {"feeRule.id"}, 0);
			
			meter.setCheckTime(Calendar.getInstance().getTime());
			meter.setPrice(feeRuleCfg.getFeeRuleCfgCalc().getPrice());
			meter.setReader(landlordUser);
		}
	}

	private void fixModelIdAndVersion(LandlordOwner model, LandlordOwnerUser landlordUser, String[] keys, int dataIndex) {
		// 1. id
		Integer id = model.getId();
		LandlordOwner dbModel = null;
		if (id == null) {
			List<Object> params = new ArrayList<Object>();
			params.add(landlordUser.getId());
			boolean needSearch = true;
			for (String key : keys) {
				Object value = ModelUtil.getProperty(model, key);
				if (value == null) {
					needSearch = false;
					break;
				}
				params.add(value);
			}
			
			if (needSearch) {
				String ql = "from " + ClassUtil.getClassName(model) + " a where a.landlordUser.id=? and a." + StringUtils.join(keys, "=? and a.") + "=?";
				QLInfo qlInfo = new QLInfo(ql, true, params);
				
				dbModel = (LandlordOwner) genericService.searchByQl(qlInfo).getData(dataIndex);
				if (dbModel != null) {
					id = dbModel.getId();
					ModelUtil.setSimpleProperty(model, "id", id);
				}
			}
		} else {
			dbModel = genericService.load(model);
		}
		
		// 2. version
		Version version = (Version)model;
		if (dbModel == null) {
			version.setVersion(1);
		} else {
			Integer dbVersion = ((Version)dbModel).getVersion();
			version.setVersion(dbVersion + 1);
		}
		
		// 3. status and landlord user
		version.setStatus(Version.STATUS_VALID);
		model.setLandlordUser(landlordUser);
	}

}
